{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 时间序列偏移和滞后\n",
    "\n",
    "本教程介绍Pandas中时间序列的shift()方法、滞后变量创建、前向后向填充等操作。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. shift()方法基础"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建时间序列\n",
    "dates = pd.date_range('2023-01-01', periods=10, freq='D')\n",
    "ts = pd.Series(range(10), index=dates)\n",
    "\n",
    "print(\"原始时间序列:\")\n",
    "print(ts)\n",
    "\n",
    "# 向前偏移（滞后）\n",
    "shifted_forward = ts.shift(2)\n",
    "print(\"\\n向前偏移2期（滞后2期）:\")\n",
    "print(shifted_forward)\n",
    "\n",
    "# 向后偏移（超前）\n",
    "shifted_backward = ts.shift(-2)\n",
    "print(\"\\n向后偏移2期（超前2期）:\")\n",
    "print(shifted_backward)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 对比原始数据和偏移数据\n",
    "comparison = pd.DataFrame({\n",
    "    'original': ts,\n",
    "    'lag_1': ts.shift(1),\n",
    "    'lag_2': ts.shift(2),\n",
    "    'lead_1': ts.shift(-1),\n",
    "    'lead_2': ts.shift(-2)\n",
    "})\n",
    "\n",
    "print(\"偏移对比:\")\n",
    "print(comparison)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. 时间频率偏移"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 按时间频率偏移\n",
    "hourly_dates = pd.date_range('2023-01-01', periods=24, freq='H')\n",
    "hourly_ts = pd.Series(range(24), index=hourly_dates)\n",
    "\n",
    "print(\"原始小时数据（前10个）:\")\n",
    "print(hourly_ts.head(10))\n",
    "\n",
    "# 按时间频率偏移\n",
    "shifted_by_freq = hourly_ts.shift(freq='2H')  # 偏移2小时\n",
    "print(\"\\n按频率偏移2小时（前10个）:\")\n",
    "print(shifted_by_freq.head(10))\n",
    "\n",
    "# 按天偏移\n",
    "shifted_by_day = hourly_ts.shift(freq='1D')  # 偏移1天\n",
    "print(\"\\n按频率偏移1天（前10个）:\")\n",
    "print(shifted_by_day.head(10))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 不同频率偏移示例\n",
    "daily_ts = pd.Series(range(10), index=pd.date_range('2023-01-01', periods=10, freq='D'))\n",
    "\n",
    "freq_shifts = {\n",
    "    '1D': '偏移1天',\n",
    "    '2D': '偏移2天',\n",
    "    '1W': '偏移1周',\n",
    "    '1M': '偏移1月',\n",
    "    '3H': '偏移3小时'\n",
    "}\n",
    "\n",
    "print(\"不同频率偏移结果:\")\n",
    "print(f\"原始首个日期: {daily_ts.index[0]}\")\n",
    "\n",
    "for freq, desc in freq_shifts.items():\n",
    "    try:\n",
    "        shifted = daily_ts.shift(freq=freq)\n",
    "        print(f\"{desc:10} -> 新首个日期: {shifted.index[0]}\")\n",
    "    except Exception as e:\n",
    "        print(f\"{desc:10} -> 错误: {str(e)[:30]}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. 滞后变量创建"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 模拟股票价格数据\n",
    "np.random.seed(42)\n",
    "dates = pd.date_range('2023-01-01', periods=20, freq='D')\n",
    "prices = 100 + np.cumsum(np.random.randn(20) * 2)\n",
    "stock_data = pd.DataFrame({'price': prices}, index=dates)\n",
    "\n",
    "# 创建滞后变量\n",
    "stock_data['price_lag1'] = stock_data['price'].shift(1)\n",
    "stock_data['price_lag2'] = stock_data['price'].shift(2)\n",
    "stock_data['price_lag3'] = stock_data['price'].shift(3)\n",
    "\n",
    "# 创建超前变量\n",
    "stock_data['price_lead1'] = stock_data['price'].shift(-1)\n",
    "stock_data['price_lead2'] = stock_data['price'].shift(-2)\n",
    "\n",
    "print(\"股票价格滞后和超前变量:\")\n",
    "print(stock_data.head(10))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 计算变化率和差值\n",
    "stock_data['price_change'] = stock_data['price'] - stock_data['price_lag1']\n",
    "stock_data['price_pct_change'] = stock_data['price'].pct_change()\n",
    "stock_data['price_diff'] = stock_data['price'].diff()\n",
    "\n",
    "# 移动平均\n",
    "stock_data['ma_3'] = stock_data['price'].rolling(3).mean()\n",
    "stock_data['ma_5'] = stock_data['price'].rolling(5).mean()\n",
    "\n",
    "print(\"\\n添加变化率和移动平均:\")\n",
    "print(stock_data[['price', 'price_change', 'price_pct_change', 'price_diff', 'ma_3', 'ma_5']].head(10))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. 填充缺失值"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建有缺失值的时间序列\n",
    "dates = pd.date_range('2023-01-01', periods=15, freq='D')\n",
    "values = [1, 2, np.nan, 4, 5, np.nan, np.nan, 8, 9, 10, np.nan, 12, 13, np.nan, 15]\n",
    "ts_with_nan = pd.Series(values, index=dates)\n",
    "\n",
    "print(\"含缺失值的时间序列:\")\n",
    "print(ts_with_nan)\n",
    "\n",
    "# 前向填充（用前一个有效值填充）\n",
    "ffill_result = ts_with_nan.fillna(method='ffill')\n",
    "print(\"\\n前向填充结果:\")\n",
    "print(ffill_result)\n",
    "\n",
    "# 后向填充（用后一个有效值填充）\n",
    "bfill_result = ts_with_nan.fillna(method='bfill')\n",
    "print(\"\\n后向填充结果:\")\n",
    "print(bfill_result)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 不同填充方法对比\n",
    "fill_comparison = pd.DataFrame({\n",
    "    'original': ts_with_nan,\n",
    "    'ffill': ts_with_nan.fillna(method='ffill'),\n",
    "    'bfill': ts_with_nan.fillna(method='bfill'),\n",
    "    'interpolate': ts_with_nan.interpolate(),\n",
    "    'mean': ts_with_nan.fillna(ts_with_nan.mean()),\n",
    "    'median': ts_with_nan.fillna(ts_with_nan.median())\n",
    "})\n",
    "\n",
    "print(\"不同填充方法对比:\")\n",
    "print(fill_comparison)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. 时间窗口操作"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建更长的时间序列\n",
    "np.random.seed(42)\n",
    "long_dates = pd.date_range('2023-01-01', periods=50, freq='D')\n",
    "long_values = np.random.randn(50).cumsum()\n",
    "long_ts = pd.Series(long_values, index=long_dates)\n",
    "\n",
    "# 滚动窗口统计\n",
    "window_stats = pd.DataFrame({\n",
    "    'value': long_ts,\n",
    "    'rolling_mean_7': long_ts.rolling(7).mean(),\n",
    "    'rolling_std_7': long_ts.rolling(7).std(),\n",
    "    'rolling_min_7': long_ts.rolling(7).min(),\n",
    "    'rolling_max_7': long_ts.rolling(7).max(),\n",
    "    'expanding_mean': long_ts.expanding().mean()\n",
    "})\n",
    "\n",
    "print(\"滚动窗口统计（前15行）:\")\n",
    "print(window_stats.head(15))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 自定义滚动函数\n",
    "def rolling_range(x):\n",
    "    return x.max() - x.min()\n",
    "\n",
    "def rolling_volatility(x):\n",
    "    return x.std() / x.mean() if x.mean() != 0 else 0\n",
    "\n",
    "# 应用自定义函数\n",
    "custom_rolling = pd.DataFrame({\n",
    "    'value': long_ts,\n",
    "    'rolling_range_5': long_ts.rolling(5).apply(rolling_range),\n",
    "    'rolling_volatility_10': long_ts.rolling(10).apply(rolling_volatility),\n",
    "    'rolling_median_7': long_ts.rolling(7).median(),\n",
    "    'rolling_quantile_75': long_ts.rolling(7).quantile(0.75)\n",
    "})\n",
    "\n",
    "print(\"\\n自定义滚动函数（前15行）:\")\n",
    "print(custom_rolling.head(15))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6. 实际应用示例"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 模拟销售数据分析\n",
    "np.random.seed(42)\n",
    "sales_dates = pd.date_range('2023-01-01', periods=100, freq='D')\n",
    "\n",
    "# 模拟销售数据（带趋势和季节性）\n",
    "trend = np.linspace(1000, 1500, 100)\n",
    "seasonal = 200 * np.sin(2 * np.pi * np.arange(100) / 30)  # 30天周期\n",
    "noise = np.random.normal(0, 100, 100)\n",
    "sales = trend + seasonal + noise\n",
    "\n",
    "sales_df = pd.DataFrame({'sales': sales}, index=sales_dates)\n",
    "\n",
    "# 创建滞后变量用于预测\n",
    "for lag in range(1, 8):  # 创建1-7天的滞后\n",
    "    sales_df[f'sales_lag_{lag}'] = sales_df['sales'].shift(lag)\n",
    "\n",
    "# 创建移动平均特征\n",
    "sales_df['ma_7'] = sales_df['sales'].rolling(7).mean()\n",
    "sales_df['ma_14'] = sales_df['sales'].rolling(14).mean()\n",
    "sales_df['ma_30'] = sales_df['sales'].rolling(30).mean()\n",
    "\n",
    "# 创建变化率特征\n",
    "sales_df['sales_change'] = sales_df['sales'].diff()\n",
    "sales_df['sales_pct_change'] = sales_df['sales'].pct_change()\n",
    "\n",
    "# 创建趋势特征\n",
    "sales_df['trend_7'] = sales_df['sales'] - sales_df['ma_7']\n",
    "sales_df['trend_14'] = sales_df['sales'] - sales_df['ma_14']\n",
    "\n",
    "print(\"销售数据特征工程结果:\")\n",
    "print(sales_df[['sales', 'sales_lag_1', 'sales_lag_7', 'ma_7', 'ma_14', 'sales_change', 'trend_7']].head(15))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 相关性分析\n",
    "correlation_cols = ['sales', 'sales_lag_1', 'sales_lag_2', 'sales_lag_3', 'sales_lag_7', 'ma_7', 'ma_14']\n",
    "correlation_matrix = sales_df[correlation_cols].corr()\n",
    "\n",
    "print(\"特征相关性矩阵:\")\n",
    "print(correlation_matrix.round(3))\n",
    "\n",
    "# 找出与销售额最相关的滞后期\n",
    "lag_correlations = {}\n",
    "for lag in range(1, 8):\n",
    "    corr = sales_df['sales'].corr(sales_df[f'sales_lag_{lag}'])\n",
    "    lag_correlations[f'lag_{lag}'] = corr\n",
    "\n",
    "print(\"\\n各滞后期与销售额的相关性:\")\n",
    "for lag, corr in lag_correlations.items():\n",
    "    print(f\"{lag}: {corr:.3f}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 可视化时间序列和滞后变量\n",
    "fig, axes = plt.subplots(2, 2, figsize=(15, 10))\n",
    "\n",
    "# 原始销售数据\n",
    "sales_df['sales'].plot(ax=axes[0,0], title='原始销售数据')\n",
    "axes[0,0].set_ylabel('销售额')\n",
    "\n",
    "# 销售额与1天滞后的对比\n",
    "sales_df[['sales', 'sales_lag_1']].plot(ax=axes[0,1], title='销售额与1天滞后对比')\n",
    "axes[0,1].set_ylabel('销售额')\n",
    "\n",
    "# 移动平均\n",
    "sales_df[['sales', 'ma_7', 'ma_14', 'ma_30']].plot(ax=axes[1,0], title='销售额与移动平均')\n",
    "axes[1,0].set_ylabel('销售额')\n",
    "\n",
    "# 销售变化率\n",
    "sales_df['sales_change'].plot(ax=axes[1,1], title='销售额日变化', kind='line')\n",
    "axes[1,1].axhline(y=0, color='red', linestyle='--', alpha=0.7)\n",
    "axes[1,1].set_ylabel('变化额')\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 简单的预测模型示例\n",
    "from sklearn.linear_model import LinearRegression\n",
    "from sklearn.metrics import mean_squared_error, r2_score\n",
    "\n",
    "# 准备特征和目标变量\n",
    "feature_cols = ['sales_lag_1', 'sales_lag_2', 'sales_lag_3', 'ma_7', 'ma_14']\n",
    "model_data = sales_df[feature_cols + ['sales']].dropna()\n",
    "\n",
    "X = model_data[feature_cols]\n",
    "y = model_data['sales']\n",
    "\n",
    "# 分割训练和测试集\n",
    "split_point = int(len(model_data) * 0.8)\n",
    "X_train, X_test = X[:split_point], X[split_point:]\n",
    "y_train, y_test = y[:split_point], y[split_point:]\n",
    "\n",
    "# 训练模型\n",
    "model = LinearRegression()\n",
    "model.fit(X_train, y_train)\n",
    "\n",
    "# 预测\n",
    "y_pred = model.predict(X_test)\n",
    "\n",
    "# 评估\n",
    "mse = mean_squared_error(y_test, y_pred)\n",
    "r2 = r2_score(y_test, y_pred)\n",
    "\n",
    "print(f\"模型评估结果:\")\n",
    "print(f\"均方误差 (MSE): {mse:.2f}\")\n",
    "print(f\"决定系数 (R²): {r2:.3f}\")\n",
    "\n",
    "# 特征重要性\n",
    "feature_importance = pd.DataFrame({\n",
    "    'feature': feature_cols,\n",
    "    'coefficient': model.coef_\n",
    "}).sort_values('coefficient', key=abs, ascending=False)\n",
    "\n",
    "print(\"\\n特征重要性:\")\n",
    "print(feature_importance)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.8.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
